﻿<#
.SYNOPSIS
This is the main script for the App-V 5 Hydration Kit.

.DESCRIPTION
This script installs Microsoft Deployment Toolkit and the Windows Assessment and Deployment
Toolkit, if they are not already installed. It then builds installation media for each
operating system provided by the user. The media contains an automated installation of the 
OS and the App-V Sequencer.

.PARAMETER DownloadADK
If this switch is specified, the full Windows Assessment and Deployment Toolkit will be downloaded.
This is equivalent to running adksetup.exe with the /layout switch.

.PARAMETER DeploymentShareRoot
The path of the folder where the MDT Deployment Share will be created.

.PARAMETER MediaRoot
The path of the folder where the MDT media (ISOs, etc.) will be created.
#>

[CmdletBinding()]
param
(
    # Whether to download the full Windows ADK.
    [Parameter(Mandatory = $false)]
    [switch]$DownloadADK,
    
    # The path of the folder where the MDT Deployment Share will be created.
    [Parameter(Mandatory = $true)]
    [string]$DeploymentShareRoot,

    # The path of the folder where the MDT media (ISOs, etc.) will be created.
    [Parameter(Mandatory = $true)]
    [string]$MediaRoot
)

Set-StrictMode -Version "latest"

[DateTime]$startTime = (Get-Date)

# The user name for Windows.
[string]$FullName = "App-V Sequencer"

# The organization name for Windows.
[string]$OrganizationName = "My Company"

# The administrator password for each Windows installation.
[string]$AdminPassword = "Password123!"

# The name of the root directory that will contain the files used by this script.
[string]$KitDirectoryName = "App-V Hydration Kit"

# The name of the directory that will contain the pre-requisites needed by this script.
[string]$PreReqDirectoryName = "Prerequisites"

# The name of the directory that will contain the operating system media.
[string]$OSDirectoryName = "OS"

# The name of the directory that will contain the pre-configured content for the deployment share.
[string]$DeploymentShareContentDirectoryName = "Deployment Share Content"

# The name of the directory that will contain the Microsoft Deployment Toolkit (MDT) 2013 installation files.
[string]$MDTDirectoryName = "MDT 2013"

# The name of the directory that will contain the Windows Assessment and Deployment Toolkit (ADK) installation files.
[string]$ADKDirectoryName = "ADK 8.1"

# The name of the directory that will contain the .NET Framework 4.5 installation files.
[string]$DotNet45DirectoryName = "DotNet 4.5"

# The name of the directory that will contain the .NET Framework 4.0 installation files.
[string]$DotNet40DirectoryName = "DotNet 4.0"

# The name of the directory that will contain the App-V Sequencer installation files.
[string]$SequencerDirectoryName = "App-V 5 Sequencer"

# The name of the directory that will contain the Windows Management Framework 3.0 installation files.
[string]$WMF3DirectoryName = "Windows Management Framework 3.0"

# The name of the directory that will contain the KB2533623 installation files.
[string]$KB2533623DirectoryName = "KB2533623"

# The name of the directory that will contain the Internet Explorer 9 installation files.
[string]$IE9DirectoryName = "IE9"


# Get the path of the folder containing this script.
[string]$ScriptDirectory = (Split-Path ($MyInvocation.MyCommand.Definition))


# Gets the full path of the directory containing the MDT installation files.
function Get-MDTFolderPath
{
    [string]$MDTFolderPath = Join-Path (Join-Path (Join-Path $ScriptDirectory $KitDirectoryName) $PreReqDirectoryName) $MDTDirectoryName
    if (!(Test-Path $MDTFolderPath)) { New-Item -ItemType Directory -Path $MDTFolderPath | Out-Null }
    return $MDTFolderPath
}


# Downloads the Microsoft Deployment Toolkit (MDT) 2013 setup files.
function Download-MDT
{
    Write-Verbose "Downloading Microsoft Deployment Toolkit 2013..."

    $client = New-Object -TypeName "System.Net.WebClient"

    $downloads = @{
                    "http://download.microsoft.com/download/B/F/5/BF5DF779-ED74-4BEC-A07E-9EB25694C6BB/MicrosoftDeploymentToolkit2013_x64.msi" = "MicrosoftDeploymentToolkit2013_x64.msi"
                    "http://download.microsoft.com/download/B/F/5/BF5DF779-ED74-4BEC-A07E-9EB25694C6BB/MicrosoftDeploymentToolkit2013_x86.msi" = "MicrosoftDeploymentToolkit2013_x86.msi"
                  }

    [string]$destFilePath = [string]::Empty
    $downloads.GetEnumerator() |
    % {
        $destFilePath = Join-Path (Get-MDTFolderPath) ($_.Value)
        Create-ParentFolder -Path $destFilePath
        if (!(Test-Path $destFilePath))
        {
            $client.DownloadFile($_.Key, $destFilePath)
        }
    }

    $client.Dispose()
}


# Installs Microsoft Deployment Toolkit 2013 for the currently-running Windows platform.
function Install-MDT
{
    [string]$msiPath = Join-Path (Get-MDTFolderPath) "MicrosoftDeploymentToolkit2013_x{0}.msi"

    # Determine whether we're on x86 or x64, and adjust the MSI name accordingly.
    if ((Get-WmiObject -Class Win32_Processor).AddressWidth -eq 64)
    {
        $msiPath = $msiPath -f "64"
    }
    else
    {
        $msiPath = $msiPath -f "86"
    }

    [string]$msiExecPath = Join-Path ([System.Environment]::SystemDirectory) "msiexec.exe"

    if ((Test-Path $msiExecPath) -and (Test-Path $msiPath))
    {
        Start-Process -FilePath $msiExecPath -ArgumentList "/qb- /i `"$msiPath`"" -WorkingDirectory ([System.IO.Path]::GetDirectoryName($msiExecPath)) -Wait
    }
}


# Determines whether MDT is installed.
function Is-MDTInstalled
{
    if (Test-RegistryValue -Path "Registry::HKLM\SOFTWARE\Microsoft\Deployment 4" -Name "Install_Dir")
    {
        [string]$mdtInstallDir = (Get-ItemProperty -Path "Registry::HKLM\SOFTWARE\Microsoft\Deployment 4" -ErrorAction SilentlyContinue).Install_Dir
        if (!([string]::IsNullOrEmpty($mdtInstallDir)))
        {
            return (Test-Path $mdtInstallDir)
        }
        else
        {
            return $false
        }
    }
    else
    {
        return $false
    }
}


# Gets the full path of the directory containing the ADK installation files.
function Get-ADKFolderPath
{
    [string]$ADKFolderPath = Join-Path (Join-Path (Join-Path $ScriptDirectory $KitDirectoryName) $PreReqDirectoryName) $ADKDirectoryName
    if (!(Test-Path $ADKFolderPath)) { New-Item -ItemType Directory -Path $ADKFolderPath | Out-Null }
    return $ADKFolderPath
}


# Downloads the setup files for the Windows Assessment and Deployment Toolkit 8.1.
function Download-ADK
{
    Write-Verbose "Downloading Microsoft Assessment and Deployment Toolkit (ADK)..."

    $client = New-Object -TypeName "System.Net.WebClient"

    $downloads = @{
                    "http://download.microsoft.com/download/6/A/E/6AEA92B0-A412-4622-983E-5B305D2EBE56/adk/adksetup.exe" = "adksetup.exe"
                  }

    if ($DownloadADK)
    { # The user chose to download the full ADK.

        Write-Verbose "The -DownloadADK switch was specified. The full ADK will be downloaded."

        if (((Get-FolderSize (Get-ADKFolderPath)) / 1GB) -lt 2.8)
        {
            Write-Verbose "The ADK download appears to be missing or incomplete."

            [string]$tempFolderPath = Get-TempFolderPath
            Write-Verbose "Downloading ADK setup from Microsoft to $tempFolderPath."

            [string]$destFilePath = [string]::Empty
            $downloads.GetEnumerator() |
            % {
                $destFilePath = Join-Path $tempFolderPath ($_.Value)
                Create-ParentFolder -Path $destFilePath
                if (!(Test-Path $destFilePath))
                {
                    $client.DownloadFile($_.Key, $destFilePath)
                }
                if (Test-Path $destFilePath)
                {
                    Write-Host "Downloading the full Windows Assessment and Deployment Kit (Windows ADK) for Windows 8.1."
                    Write-Host "This will take quite some time."
                    Start-Process -FilePath $destFilePath -ArgumentList "/layout `"$(Get-ADKFolderPath)`" /quiet /ceip off" -WorkingDirectory $tempFolderPath -Wait
                }
            }

            if (Test-Path $tempFolderPath)
            {
                Remove-Item -Path $tempFolderPath -Recurse -Force
            }
        }
        else
        { # The folder appears to be of the correct size for a full ADK download.
            Write-Verbose "The ADK appears to be fully downloaded."
        }
    }
    else
    { # The user chose not to download the full ADK.

        Write-Verbose "The -DownloadADK switch was not specified. The full ADK will not be downloaded."

        [string]$destFilePath = [string]::Empty
        $downloads.GetEnumerator() |
        % {
            $destFilePath = Join-Path (Get-ADKFolderPath) ($_.Value)
            Create-ParentFolder -Path $destFilePath
            if (!(Test-Path $destFilePath))
            {
                $client.DownloadFile($_.Key, $destFilePath)
            }
        }
    }

    $client.Dispose()
}


# Installs the Microsoft Assessment and Deployment Toolkit.
function Install-ADK
{
    [string]$exePath = Join-Path (Get-ADKFolderPath) "adksetup.exe"

    if (Test-Path $exePath)
    {
        Start-Process -FilePath $exePath -ArgumentList "/features OptionId.DeploymentTools OptionId.WindowsPreinstallationEnvironment /promptrestart /ceip off /quiet" -WorkingDirectory ([System.IO.Path]::GetDirectoryName($exePath)) -Wait
    }
}


# Determines whether the Windows ADK is installed.
function Is-ADKInstalled
{
    [string]$regKeyPath = "Registry::HKLM\SOFTWARE\Microsoft\Windows Kits\Installed Products"
    if ((Get-WmiObject -Class Win32_Processor).AddressWidth -eq 64)
    {
        $regKeyPath = "Registry::HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows Kits\Installed Products"
    }

    if ((Test-RegistryValue -Path $regKeyPath -Name "{6C870B12-6FF2-68FC-8C3B-DD177BBF3F92}") -and
        (Test-RegistryValue -Path $regKeyPath -Name "{CA93A4A4-152D-AD29-4BAD-AB9C2D74F8AE}") -and
        (Test-RegistryValue -Path $regKeyPath -Name "{6EA3CA0C-A025-FD48-D9C2-6191B9491E43}") -and
        (Test-RegistryValue -Path $regKeyPath -Name "{6FDE09DB-D711-593B-0823-D99D2A757227}") -and
        (Test-RegistryValue -Path $regKeyPath -Name "{C477F452-4589-BFC1-304B-22C63960FC40}") -and
        (Test-RegistryValue -Path $regKeyPath -Name "{B74E65FD-CC47-41C5-4B89-791A3F61942D}"))
    {
        $regKeyPath = "Registry::HKLM\SOFTWARE\Microsoft\Windows Kits\Installed Roots"
        if ((Get-WmiObject -Class Win32_Processor).AddressWidth -eq 64)
        {
            $regKeyPath = "Registry::HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows Kits\Installed Roots"
        }
        if (Test-RegistryValue -Path $regKeyPath -Name "KitsRoot81")
        {
            [string]$installDir = (Get-ItemProperty -Path $regKeyPath -ErrorAction SilentlyContinue).KitsRoot81
            if (!([string]::IsNullOrEmpty($installDir)))
            {
                return (Test-Path $installDir)
            }
            else
            {
                return $false
            }
        }
        else
        {
            return $false
        }
    }
    else
    {
        return $false
    }
}


# Gets the full path of the directory containing the Microsoft .NET Framework 4.5 installation files.
function Get-DotNet45FolderPath
{
    [string]$DotNet45FolderPath = Join-Path (Join-Path (Join-Path $ScriptDirectory $KitDirectoryName) $PreReqDirectoryName) $DotNet45DirectoryName
    if (!(Test-Path $DotNet45FolderPath)) { New-Item -ItemType Directory -Path $DotNet45FolderPath | Out-Null }
    return $DotNet45FolderPath
}

function Download-DotNet45
{
    Write-Verbose "Downloading Microsoft .NET Framework 4.5.1..."

    $client = New-Object -TypeName "System.Net.WebClient"

    $downloads = @{
                    "http://go.microsoft.com/fwlink/?LinkId=322116" = "NDP451-KB2858728-x86-x64-AllOS-ENU.exe"
                  }

    [string]$destFilePath = [string]::Empty
    $downloads.GetEnumerator() |
    % {
        $destFilePath = Join-Path (Get-DotNet45FolderPath) ($_.Value)
        Create-ParentFolder -Path $destFilePath
        if (!(Test-Path $destFilePath))
        {
            $client.DownloadFile($_.Key, $destFilePath)
        }
    }

    $client.Dispose()
}

function Install-DotNet45
{
    Write-Verbose "Installing .NET Framework 4.5.1."
    [string]$exePath = Join-Path (Get-DotNet45FolderPath) "NDP451-KB2858728-x86-x64-AllOS-ENU.exe"

    if (Test-Path $exePath)
    {
        Start-Process -FilePath $exePath -ArgumentList "/passive /promptrestart" -WorkingDirectory ([System.IO.Path]::GetDirectoryName($exePath)) -Wait
    }

}

function Is-DotNet45Installed
{
    [string]$regKeyPath = "Registry::HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full"

    return (Test-RegistryValue -Path $regKeyPath -Name "Release")
}


# Gets the full path of the directory containing the Microsoft .NET Framework 4.0 installation files.
function Get-DotNet40FolderPath
{
    [string]$DotNet40FolderPath = Join-Path (Join-Path (Join-Path $ScriptDirectory $KitDirectoryName) $PreReqDirectoryName) $DotNet40DirectoryName
    if (!(Test-Path $DotNet40FolderPath)) { New-Item -ItemType Directory -Path $DotNet40FolderPath | Out-Null }
    return $DotNet40FolderPath
}

function Download-DotNet40
{
    Write-Verbose "Downloading Microsoft .NET Framework 4.0..."

    $client = New-Object -TypeName "System.Net.WebClient"

    $downloads = @{
                    "http://download.microsoft.com/download/9/5/A/95A9616B-7A37-4AF6-BC36-D6EA96C8DAAE/dotNetFx40_Full_x86_x64.exe" = "dotNetFx40_Full_x86_x64.exe"
                  }

    [string]$destFilePath = [string]::Empty
    $downloads.GetEnumerator() |
    % {
        $destFilePath = Join-Path (Get-DotNet40FolderPath) ($_.Value)
        Create-ParentFolder -Path $destFilePath
        if (!(Test-Path $destFilePath))
        {
            $client.DownloadFile($_.Key, $destFilePath)
        }
    }

    $client.Dispose()
}

function Import-DotNet40
{
    param(
        [Parameter(Position = 0, Mandatory = $true)]
        [string]$DeploymentShareDrive
    ) 
    [string]$folderPath = ("{0}:\Applications" -f $DeploymentShareDrive)

    if (!(Test-Path $folderPath)) { return }

    [string]$longAppName = [string]::Empty

    [string]$newFolderName = "Microsoft .NET Framework 4.0"
    New-Item -Path $folderPath -Enable "True" -Name $newFolderName -Comments "" -ItemType "Folder" -Verbose -ErrorAction SilentlyContinue | Out-Null
    $folderPath = Join-Path $folderPath $newFolderName

    if (Test-Path(Join-Path (Get-DotNet40FolderPath) "dotNetFx40_Full_x86_x64.exe"))
    {
        $longAppName = "Microsoft .NET Framework 4.0"
        $newApp = Import-MDTApplication -Path $folderPath -Name $longAppName -ShortName $longAppName -Enable "True" -ApplicationSourcePath (Get-DotNet40FolderPath) -DestinationFolder $longAppName -Version "4.0" -Publisher "Microsoft" -Language "" -CommandLine "dotNetFx40_Full_x86_x64.exe /passive /norestart" -WorkingDirectory ".\Applications\$longAppName" -Verbose
        Sleep -Seconds 5
        Set-ApplicationReboot -AppGuid $newApp.guid -Reboot
    }
}


# Gets the full path of the directory containing the App-V Sequencer installation files.
function Get-SequencerFolderPath
{
    [string]$SequencerFolderPath = Join-Path (Join-Path (Join-Path $ScriptDirectory $KitDirectoryName) $PreReqDirectoryName) $SequencerDirectoryName
    if (!(Test-Path $SequencerFolderPath)) { New-Item -ItemType Directory -Path $SequencerFolderPath | Out-Null }
    return $SequencerFolderPath
}

function Import-Sequencer
{
    param(
        [Parameter(Position = 0, Mandatory = $true)]
        [string]$DeploymentShareDrive
    ) 
    [string]$folderPath = ("{0}:\Applications" -f $DeploymentShareDrive)

    if (!(Test-Path $folderPath)) { return }

    [string]$longAppName = [string]::Empty

    [string]$newFolderName = "App-V 5.0 Sequencer"
    New-Item -Path $folderPath -Enable "True" -Name $newFolderName -Comments "" -ItemType "Folder" -Verbose -ErrorAction SilentlyContinue | Out-Null
    $folderPath = Join-Path $folderPath $newFolderName

    if ((Test-Path(Join-Path (Get-SequencerFolderPath) "install.wsf")) -and (Test-Path(Join-Path (Get-SequencerFolderPath) "APPV_SEQUENCER_SETUP.EXE")))
    {
        $longAppName = "Microsoft App-V Sequencer 5.0"
        $newApp = Import-MDTApplication -Path $folderPath -Name $longAppName -ShortName $longAppName -Enable "True" -ApplicationSourcePath (Get-SequencerFolderPath) -DestinationFolder $longAppName -Version "5.0" -Publisher "Microsoft" -Language "" -CommandLine "%SystemRoot%\System32\wscript.exe install.wsf" -WorkingDirectory ".\Applications\$longAppName" -Verbose
    }
}


# Gets the full path of the directory containing the Windows Management Framework 3.0 installation files.
function Get-WMF3FolderPath
{
    [string]$WMF3FolderPath = Join-Path (Join-Path (Join-Path $ScriptDirectory $KitDirectoryName) $PreReqDirectoryName) $WMF3DirectoryName
    if (!(Test-Path $WMF3FolderPath)) { New-Item -ItemType Directory -Path $WMF3FolderPath | Out-Null }
    return $WMF3FolderPath
}

function Download-WMF3
{
    Write-Verbose "Downloading $WMF3DirectoryName..."

    $client = New-Object -TypeName "System.Net.WebClient"

    $downloads = @{
                    "http://download.microsoft.com/download/E/7/6/E76850B8-DA6E-4FF5-8CCE-A24FC513FD16/Windows6.1-KB2506143-x64.msu" = "x64\Windows6.1-KB2506143-x64.msu"
                    "http://download.microsoft.com/download/E/7/6/E76850B8-DA6E-4FF5-8CCE-A24FC513FD16/Windows6.1-KB2506143-x86.msu" = "x86\Windows6.1-KB2506143-x86.msu"
                  }

    [string]$destFilePath = [string]::Empty
    $downloads.GetEnumerator() |
    % {
        $destFilePath = Join-Path (Get-WMF3FolderPath) ($_.Value)
        Create-ParentFolder -Path $destFilePath
        if (!(Test-Path $destFilePath))
        {
            $client.DownloadFile($_.Key, $destFilePath)
        }
    }

    $client.Dispose()
}

function Import-WMF3
{
    param(
        [Parameter(Position = 0, Mandatory = $true)]
        [string]$DeploymentShareDrive
    ) 
    [string]$folderPath = ("{0}:\Applications" -f $DeploymentShareDrive)

    if (!(Test-Path $folderPath)) { return }

    [string]$longAppName = [string]::Empty

    [string]$newFolderName = $WMF3DirectoryName
    New-Item -Path $folderPath -Enable "True" -Name $newFolderName -Comments "" -ItemType "Folder" -Verbose -ErrorAction SilentlyContinue | Out-Null
    $folderPath = Join-Path $folderPath $newFolderName

    $newFolderName = "Windows 7 x86"
    New-Item -Path $folderPath -Enable "True" -Name $newFolderName -Comments "" -ItemType "Folder" -Verbose -ErrorAction SilentlyContinue | Out-Null
    if (Test-Path(Join-Path (Get-WMF3FolderPath) "x86\Windows6.1-KB2506143-x86.msu"))
    {
        $longAppName = "$WMF3DirectoryName for Windows 7 x86"
        $newApp = Import-MDTApplication -Path (Join-Path $folderPath $newFolderName) -Name $longAppName -ShortName $longAppName -Enable "True" -ApplicationSourcePath (Join-Path (Get-WMF3FolderPath) "x86") -DestinationFolder $longAppName -Version "3.0" -Publisher "Microsoft" -Language "" -CommandLine "wusa.exe Windows6.1-KB2506143-x86.msu /quiet /norestart" -WorkingDirectory ".\Applications\$longAppName" -Verbose
        Sleep -Seconds 5
        Set-ApplicationReboot -AppGuid $newApp.guid -Reboot
    }

    $newFolderName = "Windows 7 x64"
    New-Item -Path $folderPath -Enable "True" -Name $newFolderName -Comments "" -ItemType "Folder" -Verbose -ErrorAction SilentlyContinue | Out-Null

    if (Test-Path(Join-Path (Get-WMF3FolderPath) "x64\Windows6.1-KB2506143-x64.msu"))
    {
        $longAppName = "$WMF3DirectoryName for Windows 7 x64"
        $newApp = Import-MDTApplication -Path (Join-Path $folderPath $newFolderName) -Name $longAppName -ShortName $longAppName -Enable "True" -ApplicationSourcePath (Join-Path (Get-WMF3FolderPath) "x64") -DestinationFolder $longAppName -Version "3.0" -Publisher "Microsoft" -Language "" -CommandLine "wusa.exe Windows6.1-KB2506143-x64.msu /quiet /norestart" -WorkingDirectory ".\Applications\$longAppName" -Verbose
        Sleep -Seconds 5
        Set-ApplicationReboot -AppGuid $newApp.guid -Reboot
    }
}


# Gets the full path of the directory containing the Internet Explorer 9 installation files.
function Get-IE9FolderPath
{
    [string]$IE9FolderPath = Join-Path (Join-Path (Join-Path $ScriptDirectory $KitDirectoryName) $PreReqDirectoryName) $IE9DirectoryName
    if (!(Test-Path $IE9FolderPath)) { New-Item -ItemType Directory -Path $IE9FolderPath | Out-Null }
    return $IE9FolderPath
}

function Download-IE9
{
    Write-Verbose "Downloading Internet Explorer 9..."

    $client = New-Object -TypeName "System.Net.WebClient"

    $downloads = @{
                    "http://download.microsoft.com/download/C/1/6/C167B427-722E-4665-9A40-A37BC5222B0A/IE9-Windows7-x64-enu.exe" = "x64\IE9-Windows7-x64-enu.exe"
                    "http://download.microsoft.com/download/C/3/B/C3BF2EF4-E764-430C-BDCE-479F2142FC81/IE9-Windows7-x86-enu.exe" = "x86\IE9-Windows7-x86-enu.exe"
                  }

    [string]$destFilePath = [string]::Empty
    $downloads.GetEnumerator() |
    % {
        $destFilePath = Join-Path (Get-IE9FolderPath) ($_.Value)
        Create-ParentFolder -Path $destFilePath
        if (!(Test-Path $destFilePath))
        {
            $client.DownloadFile($_.Key, $destFilePath)
        }
    }

    $client.Dispose()
}


function Import-IE9
{
    param(
        [Parameter(Position = 0, Mandatory = $true)]
        [string]$DeploymentShareDrive
    ) 
    [string]$folderPath = ("{0}:\Applications" -f $DeploymentShareDrive)

    if (!(Test-Path $folderPath)) { return }

    [string]$longAppName = [string]::Empty

    [string]$newFolderName = "Internet Explorer 9"
    New-Item -Path $folderPath -Enable "True" -Name $newFolderName -Comments "" -ItemType "Folder" -Verbose -ErrorAction SilentlyContinue | Out-Null
    $folderPath = Join-Path $folderPath $newFolderName

    $newFolderName = "Windows 7 x86"
    New-Item -Path $folderPath -Enable "True" -Name $newFolderName -Comments "" -ItemType "Folder" -Verbose -ErrorAction SilentlyContinue | Out-Null
    if (Test-Path(Join-Path (Get-IE9FolderPath) "x86\IE9-Windows7-x86-enu.exe"))
    {
        $longAppName = "Internet Explorer 9 for Windows 7 x86"
        $newApp = Import-MDTApplication -Path (Join-Path $folderPath $newFolderName) -Name $longAppName -ShortName $longAppName -Enable "True" -ApplicationSourcePath (Join-Path (Get-IE9FolderPath) "x86") -DestinationFolder $longAppName -Version "9.0" -Publisher "Microsoft" -Language "" -CommandLine "IE9-Windows7-x86-enu.exe /passive /update-no /norestart" -WorkingDirectory ".\Applications\$longAppName" -Verbose
        Sleep -Seconds 5
        Set-ApplicationReboot -AppGuid $newApp.guid -Reboot
    }

    $newFolderName = "Windows 7 x64"
    New-Item -Path $folderPath -Enable "True" -Name $newFolderName -Comments "" -ItemType "Folder" -Verbose -ErrorAction SilentlyContinue | Out-Null

    if (Test-Path(Join-Path (Get-IE9FolderPath) "x64\IE9-Windows7-x64-enu.exe"))
    {
        $longAppName = "Internet Explorer 9 for Windows 7 x64"
        $newApp = Import-MDTApplication -Path (Join-Path $folderPath $newFolderName) -Name $longAppName -ShortName $longAppName -Enable "True" -ApplicationSourcePath (Join-Path (Get-IE9FolderPath) "x64") -DestinationFolder $longAppName -Version "9.0" -Publisher "Microsoft" -Language "" -CommandLine "IE9-Windows7-x64-enu.exe /passive /update-no /norestart" -WorkingDirectory ".\Applications\$longAppName" -Verbose
        Sleep -Seconds 5
        Set-ApplicationReboot -AppGuid $newApp.guid -Reboot
    }
}


# Gets the full path of the directory containing the KB2533623 installation files.
function Get-KB2533623FolderPath
{
    [string]$KB2533623FolderPath = Join-Path (Join-Path (Join-Path $ScriptDirectory $KitDirectoryName) $PreReqDirectoryName) $KB2533623DirectoryName
    if (!(Test-Path $KB2533623FolderPath)) { New-Item -ItemType Directory -Path $KB2533623FolderPath | Out-Null }
    return $KB2533623FolderPath
}

function Import-KB2533623
{
    param(
        [Parameter(Position = 0, Mandatory = $true)]
        [string]$DeploymentShareDrive
    ) 
    [string]$folderPath = ("{0}:\Packages" -f $DeploymentShareDrive)

    if (!(Test-Path $folderPath)) { return }

    [string]$longAppName = [string]::Empty

    [string]$newFolderName = "Windows 7"
    New-Item -Path $folderPath -Enable "True" -Name $newFolderName -Comments "" -ItemType "Folder" -Verbose -ErrorAction SilentlyContinue | Out-Null
    $folderPath = Join-Path $folderPath $newFolderName

    $newFolderName = "KB2533623"
    New-Item -Path $folderPath -Enable "True" -Name $newFolderName -Comments "" -ItemType "Folder" -Verbose -ErrorAction SilentlyContinue | Out-Null
    $folderPath = Join-Path $folderPath $newFolderName

    if (Test-Path(Join-Path (Get-KB2533623FolderPath) "*.msu"))
    {
        Import-MDTPackage -Path $folderPath -SourcePath (Get-KB2533623FolderPath) -Verbose
    }
}


function Get-ApplicationGuid
{
    param(
        [Parameter(Position = 0, Mandatory = $true)]
        [string]$AppName
    )

    Get-ChildItem -Path (Join-Path $DeploymentShareRoot "Control") -Filter "Applications.xml" |
    % {
        [xml]$settingsXml = Get-Content -Path $_.FullName
        [System.Xml.XmlNodeList]$groupNodes = $settingsXml.SelectNodes(("/applications/application[Name='{0}']" -f $AppName))
        foreach($node in $groupNodes)
        {
            [System.Xml.XmlElement]$nodeAsElement = [System.Xml.XmlElement]$node
            return $nodeAsElement.GetAttribute("guid")
        }
    }

}

function Set-ApplicationReboot
{
    param(
        [Parameter(Position = 0, Mandatory = $true)]
        [string]$AppGuid,
        [switch]$Reboot
    )

    Get-ChildItem -Path (Join-Path $DeploymentShareRoot "Control") -Filter "Applications.xml" |
    % {
        [xml]$settingsXml = Get-Content -Path $_.FullName
        [System.Xml.XmlNodeList]$groupNodes = $settingsXml.SelectNodes(("/applications/application[@guid='{0}']" -f $AppGuid))
        foreach($node in $groupNodes)
        {
            if ($node.SelectNodes("Reboot").Count -gt 0)
            {
                [System.Xml.XmlNodeList]$rebootElements = $node.SelectNodes("Reboot")
                foreach($rebootNode in $rebootElements)
                {
                    $rebootNode.InnerText = [string]$Reboot
                }
            }
            else
            {
                [System.Xml.XmlElement]$rebootElement = $settingsXml.CreateElement("Reboot")
                $rebootElement.InnerText = [string]$Reboot
                $node.AppendChild($rebootElement)                
            }
        }
        $settingsXml.Save($_.FullName)
    }
}

function Get-OSFolderPath
{
    [string]$OSFolderPath = Join-Path (Join-Path $ScriptDirectory $KitDirectoryName) $OSDirectoryName
    if (!(Test-Path $OSFolderPath)) { New-Item -ItemType Directory -Path $OSFolderPath | Out-Null }
    return $OSFolderPath
}

function Get-DSContentFolderPath
{
    [string]$FolderPath = Join-Path (Join-Path $ScriptDirectory $KitDirectoryName) $DeploymentShareContentDirectoryName
    if (!(Test-Path $FolderPath)) { New-Item -ItemType Directory -Path $FolderPath | Out-Null }
    return $FolderPath
}

function Create-ParentFolder
{
    param(
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [string]$Path
    )

    process
    {
        if (!(Test-Path (Split-Path $Path)))
        {
            New-Item -ItemType "Directory" -Path (Split-Path $Path)
        }
    }
}

function Get-TempFolderPath
{
    [string]$tempFolderPath = [string]::Empty
    do
    {
        $tempFolderPath = [System.IO.Path]::GetTempFileName()
        if (Test-Path -PathType Leaf -Path $tempFolderPath) { Remove-Item -Path $tempFolderPath -Force | Out-Null }
    }
    while (Test-Path $tempFolderPath)

    if (!(Test-Path $tempFolderPath)) { New-Item -ItemType Directory -Path $tempFolderPath | Out-Null }

    return $tempFolderPath
}


function Get-FolderSize([string]$path)
{
    if (Test-Path $path)
    {
        if ((Get-ChildItem -Path $path -Recurse -Force) -eq $null)
        {
            return 0
        }
        else
        {
            return (Get-ChildItem -Path $path -Recurse -Force | Measure-Object -Property "Length" -Sum).Sum
        }
    }
    else
    {
        return 0
    }
}

# Stolen from: http://stackoverflow.com/questions/5648931/test-if-registry-value-exists
Function Test-RegistryValue {
    param(
        [Alias("PSPath")]
        [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [String]$Path
        ,
        [Parameter(Position = 1, Mandatory = $true)]
        [String]$Name
        ,
        [Switch]$PassThru
    ) 

    process {
        if (Test-Path $Path) {
            $Key = Get-Item -LiteralPath $Path
            if ($Key.GetValue($Name, $null) -ne $null) {
                if ($PassThru) {
                    Get-ItemProperty $Path $Name
                } else {
                    $true
                }
            } else {
                $false
            }
        } else {
            $false
        }
    }
}

function Copy-Content
{
    param
    (
        [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $false)]
        [string]$SourceFolderPath,

        [Parameter(Position = 1, Mandatory = $true)]
        [string]$DestFolderPath
    )
    process
    {
        Write-Verbose "Copying `"$SourceFolderPath`" to `"$DestFolderPath`"..."
        Get-ChildItem -Path $SourceFolderPath | Where-Object { ($_.Attributes -band ([System.IO.FileAttributes]::Directory)) -eq ([System.IO.FileAttributes]::Directory) } | % { Copy-Content -SourceFolderPath $_.FullName -DestFolderPath (Join-Path $DestFolderPath $_.Name) }
        Get-ChildItem -Path $SourceFolderPath | Where-Object { ($_.Attributes -band ([System.IO.FileAttributes]::Directory)) -ne ([System.IO.FileAttributes]::Directory) } | % { Copy-Item -Path $_.FullName -Destination (Join-Path $DestFolderPath $_.Name) -Force  }
    }
}

function Add-XmlAttribute
{
    param
    (
        [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
        [System.Xml.XmlElement]$Element,

        [Parameter(Mandatory = $true)]
        [string]$Name,

        [Parameter(Mandatory = $false)]
        [string]$Value = ([string]::Empty)
    )
    process
    {
        [System.Xml.XmlAttribute]$newAttribute = $Element.OwnerDocument.CreateAttribute($Name)
        $newAttribute.Value = $Value
        $Element.Attributes.Append($newAttribute) | Out-Null
    }
}

function New-PowerShellScriptTask
{
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [xml]$XmlDoc,

        [Parameter(Mandatory = $true, ValueFromPipeline = $false)]
        [string]$Name,

        [Parameter(Mandatory = $true, ValueFromPipeline = $false)]
        [string]$ScriptPath
    )
    process
    {
        [System.Xml.XmlElement]$poshElement = $XmlDoc.CreateElement("step")

        Add-XmlAttribute -Element $poshElement -Name "type" -Value "BDD_RunPowerShellAction"
        Add-XmlAttribute -Element $poshElement -Name "name" -Value $Name
        Add-XmlAttribute -Element $poshElement -Name "description"
        Add-XmlAttribute -Element $poshElement -Name "disable" -Value "false"
        Add-XmlAttribute -Element $poshElement -Name "continueOnError" -Value "false"
        Add-XmlAttribute -Element $poshElement -Name "successCodeList" -Value "0 3010"
               
        [System.Xml.XmlElement]$defaultVarList = $XmlDoc.CreateElement("defaultVarList")

        [System.Xml.XmlElement]$scriptNameVariableElement = $XmlDoc.CreateElement("variable")
        Add-XmlAttribute -Element $scriptNameVariableElement -Name "name" -Value "ScriptName"
        Add-XmlAttribute -Element $scriptNameVariableElement -Name "property" -Value "ScriptName"
        $scriptNameVariableElement.InnerText = $ScriptPath
        $defaultVarList.AppendChild($scriptNameVariableElement) | Out-Null

        [System.Xml.XmlElement]$parametersVariableElement = $XmlDoc.CreateElement("variable")
        Add-XmlAttribute -Element $parametersVariableElement -Name "name" -Value "Parameters"
        Add-XmlAttribute -Element $parametersVariableElement -Name "property" -Value "Parameters"
        $defaultVarList.AppendChild($parametersVariableElement) | Out-Null

        [System.Xml.XmlElement]$packageIDVariableElement = $XmlDoc.CreateElement("variable")
        Add-XmlAttribute -Element $packageIDVariableElement -Name "name" -Value "PackageID"
        Add-XmlAttribute -Element $packageIDVariableElement -Name "property" -Value "PackageID"
        $defaultVarList.AppendChild($packageIDVariableElement) | Out-Null

        $poshElement.AppendChild($defaultVarList) | Out-Null

        [System.Xml.XmlElement]$actionElement = $XmlDoc.CreateElement("action")
        $actionElement.InnerText = "cscript.exe `"%SCRIPTROOT%\ZTIPowerShell.wsf"
        $poshElement.AppendChild($actionElement) | Out-Null

        return $poshElement
    }
}

function New-InstallApplicationTask
{
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [xml]$XmlDoc,

        [Parameter(Mandatory = $true, ValueFromPipeline = $false)]
        [string]$Name,

        [Parameter(Mandatory = $true, ValueFromPipeline = $false)]
        [string]$AppGuid
    )
    process
    {
        [System.Xml.XmlElement]$appElement = $XmlDoc.CreateElement("step")

        Add-XmlAttribute -Element $appElement -Name "type" -Value "BDD_InstallApplication"
        Add-XmlAttribute -Element $appElement -Name "name" -Value $Name
        Add-XmlAttribute -Element $appElement -Name "description"
        Add-XmlAttribute -Element $appElement -Name "disable" -Value "false"
        Add-XmlAttribute -Element $appElement -Name "continueOnError" -Value "false"
        Add-XmlAttribute -Element $appElement -Name "runIn" -Value "WinPEandFullOS"
        Add-XmlAttribute -Element $appElement -Name "successCodeList" -Value "0 3010"

        [System.Xml.XmlElement]$defaultVarList = $XmlDoc.CreateElement("defaultVarList")

        [System.Xml.XmlElement]$scriptNameVariableElement = $XmlDoc.CreateElement("variable")
        Add-XmlAttribute -Element $scriptNameVariableElement -Name "name" -Value "ApplicationGUID"
        Add-XmlAttribute -Element $scriptNameVariableElement -Name "property" -Value "ApplicationGUID"
        $scriptNameVariableElement.InnerText = $AppGuid
        $defaultVarList.AppendChild($scriptNameVariableElement) | Out-Null

        [System.Xml.XmlElement]$parametersVariableElement = $XmlDoc.CreateElement("variable")
        Add-XmlAttribute -Element $parametersVariableElement -Name "name" -Value "ApplicationSuccessCodes"
        Add-XmlAttribute -Element $parametersVariableElement -Name "property" -Value "ApplicationSuccessCodes"
        $parametersVariableElement.InnerText = "0 3010"
        $defaultVarList.AppendChild($parametersVariableElement) | Out-Null
        
        $appElement.AppendChild($defaultVarList) | Out-Null

        [System.Xml.XmlElement]$actionElement = $XmlDoc.CreateElement("action")
        $actionElement.InnerText = "cscript.exe `"%SCRIPTROOT%\ZTIApplications.wsf`""
        $appElement.AppendChild($actionElement) | Out-Null

        return $appElement
    }
}

function New-RebootTask
{
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [xml]$XmlDoc,

        [Parameter(Mandatory = $false, ValueFromPipeline = $false)]
        [string]$Name = "Restart Computer"
    )
    process
    {
        [System.Xml.XmlElement]$rebootElement = $XmlDoc.CreateElement("step")

        Add-XmlAttribute -Element $rebootElement -Name "type" -Value "SMS_TaskSequence_RebootAction"
        Add-XmlAttribute -Element $rebootElement -Name "name" -Value $Name
        Add-XmlAttribute -Element $rebootElement -Name "description"
        Add-XmlAttribute -Element $rebootElement -Name "disable" -Value "false"
        Add-XmlAttribute -Element $rebootElement -Name "continueOnError" -Value "false"
        Add-XmlAttribute -Element $rebootElement -Name "runIn" -Value "WinPEandFullOS"
        Add-XmlAttribute -Element $rebootElement -Name "successCodeList" -Value "0 3010"

        [System.Xml.XmlElement]$defaultVarList = $XmlDoc.CreateElement("defaultVarList")

        [System.Xml.XmlElement]$rebootMessageVariableElement = $XmlDoc.CreateElement("variable")
        Add-XmlAttribute -Element $rebootMessageVariableElement -Name "name" -Value "SMSRebootMessage"
        Add-XmlAttribute -Element $rebootMessageVariableElement -Name "property" -Value "Message"
        $defaultVarList.AppendChild($rebootMessageVariableElement) | Out-Null

        [System.Xml.XmlElement]$rebootTimeoutVariableElement = $XmlDoc.CreateElement("variable")
        Add-XmlAttribute -Element $rebootTimeoutVariableElement -Name "name" -Value "SMSRebootTimeout"
        Add-XmlAttribute -Element $rebootTimeoutVariableElement -Name "property" -Value "MessageTimeout"
        $rebootTimeoutVariableElement.InnerText = "60"
        $defaultVarList.AppendChild($rebootTimeoutVariableElement) | Out-Null

        [System.Xml.XmlElement]$rebootTargetVariableElement = $XmlDoc.CreateElement("variable")
        Add-XmlAttribute -Element $rebootTargetVariableElement -Name "name" -Value "SMSRebootTarget"
        Add-XmlAttribute -Element $rebootTargetVariableElement -Name "property" -Value "Target"
        $defaultVarList.AppendChild($rebootTargetVariableElement) | Out-Null

        $rebootElement.AppendChild($defaultVarList) | Out-Null

        [System.Xml.XmlElement]$actionElement = $XmlDoc.CreateElement("action")
        $actionElement.InnerText = "smsboot.exe /target:WinPE"
        $rebootElement.AppendChild($actionElement) | Out-Null

        return $rebootElement
    }
}

function Clean-TaskSequence([string]$xmlFilePath)
{
    [xml]$tsXml = Get-Content -Path $xmlFilePath

    [System.Xml.XmlNodeList]$groupNodes = $tsXml.SelectNodes("/sequence/group[@name='State Restore']")
    foreach($node in $groupNodes)
    {
        if ($node.SelectNodes("group[@name='Imaging']").Count -gt 0)
        {
            $node.RemoveChild($node.SelectSingleNode("group[@name='Imaging']")) | Out-Null
        }
        if ($node.SelectNodes("group[@name='Custom Tasks']").Count -gt 0)
        {
            $node.RemoveChild($node.SelectSingleNode("group[@name='Custom Tasks']")) | Out-Null
        }

        if ($node.SelectNodes("step[@name='Enable BitLocker']").Count -gt 0)
        {
            $node.RemoveChild($node.SelectSingleNode("step[@name='Enable BitLocker']")) | Out-Null
        }
        if ($node.SelectNodes("step[@name='Opt In to CEIP and WER']").Count -gt 0)
        {
            $node.RemoveChild($node.SelectSingleNode("step[@name='Opt In to CEIP and WER']")) | Out-Null
        }
        if ($node.SelectNodes("step[@name='Install Applications']").Count -gt 0)
        {
            $node.RemoveChild($node.SelectSingleNode("step[@name='Install Applications']")) | Out-Null
        }
        if ($node.SelectNodes("step[@name='Restore User State']").Count -gt 0)
        {
            $node.RemoveChild($node.SelectSingleNode("step[@name='Restore User State']")) | Out-Null
        }
        if ($node.SelectNodes("step[@name='Restore Groups']").Count -gt 0)
        {
            $node.RemoveChild($node.SelectSingleNode("step[@name='Restore Groups']")) | Out-Null
        }
        if ($node.SelectNodes("step[@name='Apply Local GPO Package']").Count -gt 0)
        {
            $node.RemoveChild($node.SelectSingleNode("step[@name='Apply Local GPO Package']")) | Out-Null
        }

        if ($node.SelectNodes("step[@name='Windows Update (Pre-Application Installation)']").Count -gt 0)
        {
            [System.Xml.XmlElement]$element = [System.Xml.XmlElement]$node.SelectSingleNode("step[@name='Windows Update (Pre-Application Installation)']")
            $element.SetAttribute("disable", "false")
        }
        if ($node.SelectNodes("step[@name='Windows Update (Post-Application Installation)']").Count -gt 0)
        {
            [System.Xml.XmlElement]$element = [System.Xml.XmlElement]$node.SelectSingleNode("step[@name='Windows Update (Post-Application Installation)']")
            $element.SetAttribute("disable", "false")
        }
    }

    $tsXml.Save($xmlFilePath)
}

function Add-PowerShellTaskSequenceStep
{
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [string]$XmlFilePath,

        [Parameter(Mandatory = $true, ValueFromPipeline = $false)]
        [string]$RootGroupName,

        [Parameter(Mandatory = $true, ValueFromPipeline = $false)]
        [string]$AfterStepName,

        [Parameter(Mandatory = $true, ValueFromPipeline = $false)]
        [string]$NewStepName,

        [Parameter(Mandatory = $true, ValueFromPipeline = $false)]
        [string]$ScriptPath
    )
    process
    {
        [xml]$tsXml = Get-Content -Path $XmlFilePath
        [bool]$returnValue = $false
        [System.Xml.XmlNodeList]$groupNodes = $tsXml.SelectNodes("/sequence/group[@name='$RootGroupName']")
        foreach($node in $groupNodes)
        {
            if ($node.SelectNodes("step[@name='$AfterStepName']").Count -gt 0)
            {
                [System.Xml.XmlElement]$registryCustomizations = New-PowerShellScriptTask -XmlDoc $tsXml -Name $NewStepName -ScriptPath $ScriptPath

                [System.Xml.XmlNode]$tattooNode = $node.SelectSingleNode("step[@name='$AfterStepName']")
                $tattooNode.ParentNode.InsertAfter($registryCustomizations, $tattooNode) | Out-Null
                $returnValue = $true
            }
        }
        $tsXml.Save($XmlFilePath)
        return $returnValue
    }
}

function Add-InstallAppTaskSequenceStep
{
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [string]$XmlFilePath,

        [Parameter(Mandatory = $true, ValueFromPipeline = $false)]
        [string]$RootGroupName,

        [Parameter(Mandatory = $true, ValueFromPipeline = $false)]
        [string]$AfterStepName,

        [Parameter(Mandatory = $true, ValueFromPipeline = $false)]
        [string]$NewStepName,

        [Parameter(Mandatory = $true, ValueFromPipeline = $false)]
        [string]$AppGuid
    )
    process
    {
        [xml]$tsXml = Get-Content -Path $XmlFilePath
        [bool]$returnValue = $false
        [System.Xml.XmlNodeList]$groupNodes = $tsXml.SelectNodes("/sequence/group[@name='$RootGroupName']")
        foreach($node in $groupNodes)
        {
            if ($node.SelectNodes("step[@name='$AfterStepName']").Count -gt 0)
            {
                [System.Xml.XmlElement]$registryCustomizations = New-InstallApplicationTask -XmlDoc $tsXml -Name $NewStepName -AppGuid $AppGuid

                [System.Xml.XmlNode]$tattooNode = $node.SelectSingleNode("step[@name='$AfterStepName']")
                $tattooNode.ParentNode.InsertAfter($registryCustomizations, $tattooNode) | Out-Null
                $returnValue = $true
            }
        }

        $tsXml.Save($XmlFilePath)
        return $returnValue
    }
}

function Add-RebootTaskSequenceStep
{
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [string]$XmlFilePath,

        [Parameter(Mandatory = $true, ValueFromPipeline = $false)]
        [string]$RootGroupName,

        [Parameter(Mandatory = $true, ValueFromPipeline = $false)]
        [string]$AfterStepName,

        [Parameter(Mandatory = $false, ValueFromPipeline = $false)]
        [string]$NewStepName = "Restart Computer"
    )
    process
    {
        [xml]$tsXml = Get-Content -Path $XmlFilePath
        [bool]$returnValue = $false
        [System.Xml.XmlNodeList]$groupNodes = $tsXml.SelectNodes("/sequence/group[@name='$RootGroupName']")
        foreach($node in $groupNodes)
        {
            if ($node.SelectNodes("step[@name='$AfterStepName']").Count -gt 0)
            {
                [System.Xml.XmlElement]$rebootStep = New-RebootTask -XmlDoc $tsXml -Name $NewStepName

                [System.Xml.XmlNode]$tattooNode = $node.SelectSingleNode("step[@name='$AfterStepName']")
                $tattooNode.ParentNode.InsertAfter($rebootStep, $tattooNode) | Out-Null
                $returnValue = $true
            }
        }

        $tsXml.Save($XmlFilePath)
        return $returnValue
    }
}

function Set-TaskSequenceID
{
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [string]$CustomSettingsFilePath,

        [Parameter(Mandatory = $true, ValueFromPipeline = $false)]
        [string]$TaskSequenceID,

        [switch]$SkipTaskSequence
    )
    process
    {
        [string[]]$YesOrNo = ("NO", "YES")
        $customSettingsIni = Get-Content -Path $CustomSettingsFilePath

        if (($customSettingsIni | Select-String -Pattern "TaskSequenceID") -ne $null)
        {
            $customSettingsIni[(($customSettingsIni | Select-String –Pattern "TaskSequenceID=").LineNumber - 1)] = "TaskSequenceID=$TaskSequenceID"
        }
        ## TODO: What if TaskSequenceID line is not found?

        if (($customSettingsIni | Select-String -Pattern "SkipTaskSequence") -ne $null)
        {
            $customSettingsIni[(($customSettingsIni | Select-String –Pattern "SkipTaskSequence=").LineNumber - 1)] = "SkipTaskSequence=$($YesOrNo[([int]$SkipTaskSequence.ToBool())])"
        }
        ## TODO: What if SkipTaskSequence line is not found?

        $customSettingsIni | Set-Content -Path $CustomSettingsFilePath
    }
}

if (!(Is-DotNet45Installed))
{
    Download-DotNet45
    Install-DotNet45
}

if (Is-DotNet45Installed)
{
    Write-Host ".NET Framework 4.5 is installed."

    if (!(Is-ADKInstalled))
    {
        Download-ADK
        Install-ADK
    }

    if (Is-ADKInstalled)
    {
        Write-Host "Windows ADK is installed."

        if (!(Is-MDTInstalled))
        {
            Download-MDT
            Install-MDT
        }

        if (Is-MDTInstalled)
        {
            Write-Host "MDT is installed!"
        }
    }
}

if (Test-Path $DeploymentShareRoot)
{
    Write-Warning "$DeploymentShareRoot already exists. The script will now abort."
    exit
}

if (!(Test-Path $DeploymentShareRoot))
{
    New-Item -ItemType "Directory" -Path $DeploymentShareRoot | Out-Null
}

## TODO: Might want to share the deployment share folder, but it isn't necessary for this kit.
<# Write-Host ("Machine Name: {0}" -f ([Environment]::MachineName)) #>

[string]$mdtInstallDir = (Get-ItemProperty -Path "Registry::HKLM\SOFTWARE\Microsoft\Deployment 4" -ErrorAction SilentlyContinue).Install_Dir
if (!([string]::IsNullOrEmpty($mdtInstallDir)))
{
    [string]$modulePath = Join-Path $mdtInstallDir "bin\MicrosoftDeploymentToolkit.psd1"
    if (Test-Path $modulePath)
    {
        Import-Module $modulePath
    }
    else
    {
        Write-Error "Unable to import MDT PowerShell module."
        exit
    }
}
else
{
    Write-Error "Unable to find MDT installation directory."
    exit
}

[bool]$addPersistentDrive = $false
[string]$driveName = [string]::Empty

if (@(Get-MDTPersistentDrive | Select-Object -ExpandProperty "Path") | Select-String ($DeploymentShareRoot -replace "\\", "\\"))
{ # There is already an MDT persistent drive for this deployment share.
    Write-Verbose "There is already a persistent drive for this deployment share."
    [Microsoft.BDD.PSSnapIn.MDTDriveSettings[]]$persistentDrives = @(Get-MDTPersistentDrive)
    foreach($drv in $persistentDrives)
    {
        if ($drv.Path -eq $DeploymentShareRoot) { $driveName = $drv.Name }
    }
}
else
{ # There is not an MDT persistent drive for this deployment share.
    Write-Verbose "There is not a persistent drive for this deployment share."
    $addPersistentDrive = $true
    [int]$driveNum = 0
    do
    {
        $driveNum++
        $driveName = ("DS{0:000}" -f $driveNum)
    } while (((Get-PSDrive -Name $driveName -ErrorAction SilentlyContinue) -ne $null) -or (@(Get-MDTPersistentDrive | Select-Object -ExpandProperty Name) | Select-String $driveName))
}

## TODO: Specify the share path for the deployment share.
[System.Management.Automation.PSDriveInfo]$newDrive = New-PSDrive -Name $driveName -PSProvider "MDTProvider" -Root $DeploymentShareRoot -Description "App-V 5 sequencing machines" <#-NetworkPath ""#> -Verbose
if ($addPersistentDrive) { $newDrive | Add-MDTPersistentDrive -Verbose }

# Copy the pre-configured deployment share content to the newly-created deployment share.
Copy-Content (Get-DSContentFolderPath) ($DeploymentShareRoot)

# Download Windows Management Framework 3.0 and import it into the deployment share.
Download-WMF3
Import-WMF3 -DeploymentShareDrive $driveName

# Download Internet Explorer 9 and import it into the deployment share.
Download-IE9
Import-IE9 -DeploymentShareDrive $driveName

# Download .NET Framework 4.0 and import it into the deployment share.
Download-DotNet40
Import-DotNet40 -DeploymentShareDrive $driveName

# Import KB2533623 into the deployment share.
Import-KB2533623 -DeploymentShareDrive $driveName

# Import the App-V Sequencer into the deployment share.
Import-Sequencer -DeploymentShareDrive $driveName

# Find all of the install.wim files in the set of operating systems provided by the user.
[System.IO.FileInfo[]]$installWims = Get-ChildItem (Get-OSFolderPath) |
Where-Object { ($_.Attributes -band ([System.IO.FileAttributes]::Directory)) -eq ([System.IO.FileAttributes]::Directory) } |
% { Get-ChildItem -Path $_.FullName -Recurse -Include "install.wim" }

foreach($wimInfo in $installWims)
{ # Process each install.wim file found in the operating systems (OS) folder.

    Write-Host "Examining $($wimInfo.FullName)..."

    [string]$dismOutput = dism /Get-WimInfo /WimFile:"$($wimInfo.FullName)" /index:1 | Out-String
    [string]$regExPattern = "(?s)Name : (?<name>[^\r\n]+).*Architecture : (?<arch>[^\r\n]+).*Version : (?<version>[^\r\n]+).*ServicePack Level : (?<sp>[^\r\n]+)"

    if ($dismOutput -match $regExPattern)
    { # We seem to have found valid information in the output from DISM.

        [string]$osName = (Get-Culture).TextInfo.ToTitleCase($matches.name.Trim().ToLower())
        [string]$servicePack = $matches.sp.Trim()
        [string]$arch = $matches.arch.Trim()
        [string]$version = $matches.version.Trim()

        # Display basic information about the OS based upon the install.wim data.
        Write-Host "`t          Name : $osName"
        Write-Host "`t  Service Pack : $servicePack"
        Write-Host "`t       Version : $version"
        Write-Host "`t  Architecture : $arch"

        # Make a user-friendly string for the service pack number.
        # If the service pack in the install.wim is 0, we will assume this is the 
        # General Availability (GA) release of the operating system.
        if ($servicePack -eq "0")
        {
            $servicePack = "GA"
        }
        else
        {
            $servicePack = "SP{0}" -f $servicePack
        }

        # Create a long name for this specific OS, e.g., "Windows 7 Enterprise SP1 x86."
        [string]$longOSName = ("{0} {1} {2}" -f $osName, $servicePack, $arch)

        # Create a shortened version of the full OS name, e.g., "Win 7 Ent SP1 x86."
        [string]$shortOSName = $longOSName
        $shortOSName = $shortOSName -replace "Windows", "Win"
        $shortOSName = $shortOSName -replace "Enterprise", "Ent"
        $shortOSName = $shortOSName -replace "Professional", "Pro"

        # Construct the path of the folder where operating systems are stored in the deployment share.
        [string]$osRootFolder = ("{0}:\Operating Systems" -f $driveName)

        if (Test-Path $osRootFolder)
        {
            [string]$archAndSP = ("{0} {1}" -f $arch, $servicePack)

            # Create a folder in the MDT console tree for the OS Name (e.g., "Windows 7 Enterprise").
            if (!(Test-Path (Join-Path $osRootFolder $osName)))
            {
                New-Item -Path $osRootFolder -Enable "True" -Name $osName -Comments "" -ItemType "Folder" -Verbose | Out-Null
            }

            # Create a folder in the MDT console tree for the architecture and service pack, under the OS folder (e.g., "x86 SP1").
            # Import the operating system into this architecture/SP folder.
            # The folder in the actual file system will be the short OS name, created above, without spaces, e.g., "Win7EntSP1x86."
            if (!(Test-Path (Join-Path (Join-Path $osRootFolder $osName) $archAndSP)))
            {
                New-Item -Path (Join-Path $osRootFolder $osName) -Enable "True" -Name $archAndSP -Comments "" -ItemType "Folder" -Verbose | Out-Null
                Import-MDTOperatingSystem -Path (Join-Path (Join-Path $osRootFolder $osName) $archAndSP) -SourcePath ($wimInfo.Directory.Parent.FullName) -DestinationFolder ($shortOSName -replace " ", "")
            }

            [System.Xml.XmlElement]$includeElement = $null

            [xml]$selProfileDefinition = [xml]"<SelectionProfile></SelectionProfile>"
            if (([Version]$version) -lt ([Version]"6.2"))
            {
                Write-Verbose "This is not Windows 8 or above."

                Write-Verbose "Adding Windows Management Framework 3.0."
                $includeElement = $selProfileDefinition.CreateElement("Include")
                $includeElement.SetAttribute("path", "Applications\Windows Management Framework 3.0\Windows 7 $arch")
                $selProfileDefinition.DocumentElement.AppendChild($includeElement) | Out-Null

                Write-Verbose "Adding Internet Explorer 9."
                $includeElement = $selProfileDefinition.CreateElement("Include")
                $includeElement.SetAttribute("path", "Applications\Internet Explorer 9\Windows 7 $arch")
                $selProfileDefinition.DocumentElement.AppendChild($includeElement) | Out-Null

                Write-Verbose "Adding Microsoft .NET Framework 4.0."
                $includeElement = $selProfileDefinition.CreateElement("Include")
                $includeElement.SetAttribute("path", "Applications\Microsoft .NET Framework 4.0")
                $selProfileDefinition.DocumentElement.AppendChild($includeElement) | Out-Null

                Write-Verbose "Adding Windows 7 update packages."
                $includeElement = $selProfileDefinition.CreateElement("Include")
                $includeElement.SetAttribute("path", "Packages\Windows 7")
                $selProfileDefinition.DocumentElement.AppendChild($includeElement) | Out-Null
            }

            $includeElement = $selProfileDefinition.CreateElement("Include")
            $includeElement.SetAttribute("path", "Applications\App-V 5.0 Sequencer")
            $selProfileDefinition.DocumentElement.AppendChild($includeElement) | Out-Null

            $includeElement = $selProfileDefinition.CreateElement("Include")
            $includeElement.SetAttribute("path", "Operating Systems\$osName\$archAndSP")
            $selProfileDefinition.DocumentElement.AppendChild($includeElement) | Out-Null
            
            $includeElement = $selProfileDefinition.CreateElement("Include")
            $includeElement.SetAttribute("path", "Task Sequences")
            $selProfileDefinition.DocumentElement.AppendChild($includeElement) | Out-Null

            [string]$selProfileRootFolder = ("{0}:\Selection Profiles" -f $driveName)
            [string]$selProfileName = "App-V 5.0 $osName $archAndSP"
            New-Item -Path $selProfileRootFolder -Enable "True" -Name $selProfileName -Comments "Everything needed for sequencing with App-V 5.0 on $osName $archAndSP." -Definition ($selProfileDefinition.InnerXml) -ReadOnly "False" -Verbose | Out-Null

            [string]$taskSequenceRootFolder = ("{0}:\Task Sequences" -f $driveName)
            
            [string]$encodedOSName = $osName -replace "Windows", "W"
            $encodedOSName = $encodedOSName -replace "Enterprise", "E"
            $encodedOSName = $encodedOSName -replace "Professional", "P"
            $encodedOSName = $encodedOSName -replace " ", ""
            [string]$taskSequenceID = ("AV5{0}{1}{2}" -f $encodedOSName, $servicePack, $arch)
            
            if (Test-Path (Join-Path (Join-Path $osRootFolder $osName) $archAndSP))
            {
                [string]$psPath = Get-ChildItem -Path (Join-Path (Join-Path $osRootFolder $osName) $archAndSP) | Select-Object -ExpandProperty PSPath
                [string]$psProvider = Get-ChildItem -Path (Join-Path (Join-Path $osRootFolder $osName) $archAndSP) | Select-Object -ExpandProperty PSProvider

                [string]$wimPath = $psPath -replace ($psProvider -replace "\\", "\\"), ""
                while ($wimPath[0] -eq ":") { $wimPath = $wimPath.Substring(1) }

                [string]$taskSequenceName = "App-V 5.0 on $osName $archAndSP"
                if ((Get-Item -Path (Join-Path $taskSequenceRootFolder $taskSequenceName) -ErrorAction SilentlyContinue) -ne $null)
                {
                    Write-Verbose "Task sequence `"$taskSequenceName`" already exists. Deleting it."
                    Remove-Item -Path (Join-Path $taskSequenceRootFolder $taskSequenceName) -Verbose
                }

                if ((Get-Item -Path (Join-Path $taskSequenceRootFolder $taskSequenceName) -ErrorAction SilentlyContinue) -eq $null)
                {
                    $newTs = Import-MDTTaskSequence -Path $taskSequenceRootFolder -Template "Client.xml" -Name $taskSequenceName -ID $taskSequenceID -Comments "Sets up a sequencing machine for App-V 5.0 on $osName $archAndSP." -Version "1.0" -OperatingSystemPath $wimPath -FullName $FullName -OrgName $OrganizationName -HomePage "about:blank" -AdminPassword $AdminPassword -Verbose

                    Get-ChildItem -Path ($newTs.GetPhysicalSourcePath()) -Filter "ts.xml" |
                    % {
                        Clean-TaskSequence $_.FullName

                        [string]$lastStepName = "Tattoo"
                        [string]$stepName = [string]::Empty

                        if (([Version]$version) -lt ([Version]"6.2"))
                        {
                            Write-Verbose "This is not Windows 8 or above."

                            Write-Verbose "Adding Internet Explorer 9."

                            [string]$longAppName = "Internet Explorer 9 for Windows 7 $arch"

                            Write-Verbose "Looking up app GUID for `"$longAppName.`""
                            [string]$guid = Get-ApplicationGuid -AppName $longAppName
                            if (!([string]::IsNullOrEmpty($guid)))
                            {
                                $stepName = "Install Internet Explorer 9"
                                if (Add-InstallAppTaskSequenceStep -XmlFilePath $_.FullName -RootGroupName "State Restore" -AfterStepName $lastStepName -NewStepName $stepName -AppGuid $guid)
                                {
                                    $lastStepName = $stepName
                                }
                            }

                            Write-Verbose "Adding Microsoft .NET Framework 4.0."

                            [string]$longAppName = "Microsoft .NET Framework 4.0"

                            Write-Verbose "Looking up app GUID for `"$longAppName.`""
                            [string]$guid = Get-ApplicationGuid -AppName $longAppName
                            if (!([string]::IsNullOrEmpty($guid)))
                            {
                                $stepName = "Install Microsoft .NET Framework 4.0"
                                if (Add-InstallAppTaskSequenceStep -XmlFilePath $_.FullName -RootGroupName "State Restore" -AfterStepName $lastStepName -NewStepName $stepName -AppGuid $guid)
                                {
                                    $lastStepName = $stepName
                                }
                            }


                            Write-Verbose "Adding $WMF3DirectoryName."

                            [string]$longAppName = "$WMF3DirectoryName for Windows 7 $arch"

                            Write-Verbose "Looking up app GUID for `"$longAppName.`""
                            [string]$guid = Get-ApplicationGuid -AppName $longAppName
                            if (!([string]::IsNullOrEmpty($guid)))
                            {
                                $stepName = "Install $WMF3DirectoryName"
                                if (Add-InstallAppTaskSequenceStep -XmlFilePath $_.FullName -RootGroupName "State Restore" -AfterStepName $lastStepName -NewStepName $stepName -AppGuid $guid)
                                {
                                    $lastStepName = $stepName
                                }
                            }
                        }

                        $lastStepName = "Windows Update (Pre-Application Installation)"

                        $stepName = "PoSh - Registry Customizations"
                        if (Add-PowerShellTaskSequenceStep -XmlFilePath $_.FullName -RootGroupName "State Restore" -AfterStepName $lastStepName -NewStepName $stepName -ScriptPath "Sequencing-Registry-Customizations.ps1")
                        {
                            $lastStepName = $stepName
                        }

                        [string]$longAppName = "Microsoft App-V Sequencer 5.0"
                        Write-Verbose "Adding $longAppName."
                        Write-Verbose "Looking up app GUID for `"$longAppName.`""
                        [string]$guid = Get-ApplicationGuid -AppName $longAppName
                        if (!([string]::IsNullOrEmpty($guid)))
                        {
                            $stepName = "Install App-V 5 Sequencer"
                            if (Add-InstallAppTaskSequenceStep -XmlFilePath $_.FullName -RootGroupName "State Restore" -AfterStepName $lastStepName -NewStepName $stepName -AppGuid $guid)
                            {
                                $lastStepName = $stepName
                            }
                        }


                        $stepName = "PoSh - Format Fixed Disks"
                        if (Add-PowerShellTaskSequenceStep -XmlFilePath $_.FullName -RootGroupName "State Restore" -AfterStepName $lastStepName -NewStepName $stepName -ScriptPath "Format-Fixed-Disks.ps1")
                        {
                            $lastStepName = $stepName
                        }

                        $stepName = "Restart Computer"
                        if (Add-RebootTaskSequenceStep -XmlFilePath $_.FullName -RootGroupName "State Restore" -AfterStepName $lastStepName -NewStepName $stepName)
                        {
                            $lastStepName = $stepName
                        }

                        $lastStepName = "Windows Update (Post-Application Installation)"
                        
                        $stepName = "PoSh - Disable Services for Sequencing"
                        if (Add-PowerShellTaskSequenceStep -XmlFilePath $_.FullName -RootGroupName "State Restore" -AfterStepName $lastStepName -NewStepName $stepName -ScriptPath "Disable-Services-for-Sequencing.ps1")
                        {
                            $lastStepName = $stepName
                        }

                        $stepName = "PoSh - Configure Administrator Account"
                        if (Add-PowerShellTaskSequenceStep -XmlFilePath $_.FullName -RootGroupName "State Restore" -AfterStepName $lastStepName -NewStepName $stepName -ScriptPath "Configure-AdminAccount.ps1")
                        {
                            $lastStepName = $stepName
                        }
                    }
                }
            }

            [string]$mediaName = [string]::Empty

            [int]$mediaNum = 0
            do
            {
                $mediaNum++
                $mediaName = ("MEDIA{0:000}" -f $mediaNum)
            } while (((Get-PSDrive -Name $mediaName -ErrorAction SilentlyContinue) -ne $null) -or (@(Get-ChildItem ("{0}:\Media" -f $driveName) | Select-Object -ExpandProperty "Name") | Select-String $mediaName))
            
            if (!(Test-Path $MediaRoot))
            {
                New-Item -ItemType "Directory" -Path $MediaRoot | Out-Null
            }

            [string]$mediaDirectory = Join-Path $MediaRoot $mediaName
            if (!(Test-Path $mediaDirectory))
            {
                New-Item -ItemType "Directory" -Path $mediaDirectory | Out-Null
            }

            New-Item -Path (Join-Path $mediaDirectory "Content\Deploy") -ItemType Directory | Out-Null
            New-Item -Path ("{0}:\Media" -f $driveName) -Enable $true -Name $mediaName -Comments "Deployment media for App-V 5 Sequencer on $osName $archAndSP" -Root $mediaDirectory -SelectionProfile "App-V 5.0 $osName $archAndSP" -SupportX86 ([string]($arch -eq "x86")) -SupportX64 ([string]($arch -eq "x64")) -GenerateISO "True" -ISOName ("App-V 5 Sequencer {0} {1}.iso" -f $osName, $archAndSP) -Verbose | Out-Null
            New-PSDrive -Name $mediaName -PSProvider "MDTProvider" -Root (Join-Path $mediaDirectory "Content\Deploy") -Description "Deployment media for App-V 5 Sequencer on $osName $archAndSP" -Force -Verbose
            
            Get-ChildItem -Path (Join-Path $DeploymentShareRoot "Control") -Filter "*.ini" | Copy-Item -Force -Destination (Join-Path $mediaDirectory "Content\Deploy\Control")

            Get-ChildItem -Path (Join-Path $mediaDirectory "Content\Deploy\Control") -Filter "CustomSettings.ini" | % { Set-TaskSequenceID -CustomSettingsFilePath $_.FullName -TaskSequenceID $taskSequenceID -SkipTaskSequence }

            Update-MDTMedia -Path ("{0}:\Media\{1}" -f $driveName, $mediaName) -Verbose

            # Fix the Settings.xml file.
            Get-ChildItem -Path (Join-Path $mediaDirectory "Content\Deploy\Control") -Filter "Settings.xml" |
            % {
                [xml]$settingsXml = Get-Content -Path $_.FullName

                [System.Xml.XmlNodeList]$selProfileNodes = $settingsXml.SelectNodes("/Settings/Boot.x86.SelectionProfile")
                foreach($profileNode in $selProfileNodes)
                {
                    $profileNode.InnerText = "Nothing"
                }
                [System.Xml.XmlNodeList]$selProfileNodes = $settingsXml.SelectNodes("/Settings/Boot.x64.SelectionProfile")
                foreach($profileNode in $selProfileNodes)
                {
                    $profileNode.InnerText = "Nothing"
                }

                $settingsXml.Save($_.FullName)
            }

            # Now that the Settings.xml file has been generated and fixed, we need to update the media again.
            Update-MDTMedia -Path ("{0}:\Media\{1}" -f $driveName, $mediaName) -Verbose
        }                
    }
    else
    {
        Write-Host "Unable to match DISM result to pattern."
        Write-Host $dismOutput
    }
}


# Fix the Settings.xml file for the entire deployment share.
Get-ChildItem -Path (Join-Path $DeploymentShareRoot "Control") -Filter "Settings.xml" |
% {
    [xml]$settingsXml = Get-Content -Path $_.FullName

    # Set the x86 boot image selection profile to Nothing, so no packages and drivers are imported into the image.
    [System.Xml.XmlNodeList]$selProfileNodes = $settingsXml.SelectNodes("/Settings/Boot.x86.SelectionProfile")
    foreach($profileNode in $selProfileNodes)
    {
        $profileNode.InnerText = "Nothing"
    }

    # Set the x64 boot image selection profile to Nothing, so no packages and drivers are imported into the image.
    [System.Xml.XmlNodeList]$selProfileNodes = $settingsXml.SelectNodes("/Settings/Boot.x64.SelectionProfile")
    foreach($profileNode in $selProfileNodes)
    {
        $profileNode.InnerText = "Nothing"
    }

    $settingsXml.Save($_.FullName)
}


<#
# We don't technically need to update the deployment share, since we will use the generated media.
# Here is the command anyway.
Update-MDTDeploymentShare -Path ("{0}:" -f $driveName)
#>

$newDrive | Remove-PSDrive

Remove-Module -Name "MicrosoftDeploymentToolkit"

Write-Host ("Process completed in {0:N2} minutes." -f ((Get-Date).Subtract($startTime).TotalMinutes))